package com.ssf.framework.shiro.session.mgt;

import com.raiyee.hi.boss.redis.RedisUtil;
import com.raiyee.hi.boss.util.SerializeUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import redis.clients.jedis.ShardedJedis;

import java.util.*;

/**
 * Created by liolay on 15-7-27.
 */
public class JedisShiroCache<K, V> implements Cache<K, V> {
    private static final String REDIS_SHIRO_CACHE = "shiro-cache:";
    private String name;

    public JedisShiroCache(String name) {
        this.name = name;
    }

    public String getName() {
        if (name == null)
            return "";
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public V get(K key) throws CacheException {
        final byte[] byteKey = SerializeUtil.serialize(getCacheKey(key));
        byte[] byteValue = RedisUtil.post(new RedisUtil.JedisPostFunction<byte[]>() {
            @Override
            public byte[] post(ShardedJedis jedis) {
                return jedis.get(byteKey);
            }
        });
        return byteValue == null ? null : (V) SerializeUtil.unserialize(byteValue);
    }

    @Override
    public V put(final K key, final V value) throws CacheException {
        V previos = get(key);
        RedisUtil.post(new RedisUtil.JedisPostFunction<Object>() {
            @Override
            public Object post(ShardedJedis jedis) {
                return jedis.set(SerializeUtil.serialize(getCacheKey(key)), SerializeUtil.serialize(value));
            }
        });
        return previos;
    }

    @Override
    public V remove(final K key) throws CacheException {
        V previos = get(key);
        RedisUtil.post(new RedisUtil.JedisPostFunction<Object>() {
            @Override
            public Object post(ShardedJedis jedis) {
                return jedis.del(SerializeUtil.serialize(getCacheKey(key)));
            }
        });
        return previos;
    }

    @Override
    public void clear() throws CacheException {
        final byte[] keysPattern = SerializeUtil.serialize(REDIS_SHIRO_CACHE
                + "*");
        RedisUtil.post(new RedisUtil.JedisPostFunction<Object>() {
            @Override
            public Object post(ShardedJedis jedis) {
                return jedis.del(keysPattern);
            }
        });
    }

    @Override
    public int size() {
        if (keys() == null)
            return 0;
        return keys().size();
    }

    @Override
    public Set<K> keys() {
        Set<byte[]> byteSet = RedisUtil.post(new RedisUtil.JedisPostFunction<Set<byte[]>>() {
            @Override
            public Set<byte[]> post(ShardedJedis jedis) {
                return jedis.hkeys(SerializeUtil.serialize(REDIS_SHIRO_CACHE + "*"));
            }
        });

        Set<K> keys = new HashSet<K>();
        for (byte[] bs : byteSet) {
            keys.add((K) SerializeUtil.unserialize(bs));
        }
        return keys;
    }

    @Override
    public Collection<V> values() {
        final Set<byte[]> byteSet = RedisUtil.post(new RedisUtil.JedisPostFunction<Set<byte[]>>() {
            @Override
            public Set<byte[]> post(ShardedJedis jedis) {
                return jedis.hkeys(SerializeUtil.serialize(REDIS_SHIRO_CACHE + "*"));
            }
        });

        return RedisUtil.post(new RedisUtil.JedisPostFunction<List<V>>() {
            @Override
            public List<V> post(ShardedJedis jedis) {
                List<V> result = new LinkedList<V>();
                for (byte[] bs : byteSet) {
                    result.add((V) SerializeUtil.unserialize(jedis.get(bs)));
                }
                return result;
            }
        });

    }

    private String getCacheKey(Object key) {
        return REDIS_SHIRO_CACHE + getName() + ":" + key;
    }
}
